package cn.rubintry.flow.bus

import android.app.Activity
import android.app.Application
import android.os.Bundle
import android.util.SparseArray
import androidx.core.util.containsValue
import androidx.core.util.valueIterator
import cn.rubintry.flow.bus.annotation.Bus
import cn.rubintry.flow.bus.annotation.ThreadMode
import cn.rubintry.flow.bus.core.FlowData
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import java.lang.reflect.Method
import java.util.concurrent.LinkedBlockingDeque

class FlowBus private constructor(){

    private val flowDataDeque : LinkedBlockingDeque<FlowData> = LinkedBlockingDeque()
    private val flowDeque : LinkedBlockingDeque<Flow<FlowData>> = LinkedBlockingDeque()

    private val subscribers = SparseArray<String>()

    private var activityIndex = 0

    private val activityMap = mutableMapOf<String , Activity>()

    init {

        CoroutineScope(Dispatchers.Default).launch {
            while (true){
                try {
                    val data: FlowData = flowDataDeque.take()
                    flowDeque.put(data.asFlow())
                }catch (e: Exception){

                }
            }
        }

        CoroutineScope(Dispatchers.Default).launch {
            while (true){
                try {
                    val flow = flowDeque.take()
                    flow?.collect { busData ->
                        parseBusInfo(busData)
                    }

                }catch (e : Exception){

                }
            }
        }
    }



    companion object{
        @Volatile
        private var instance: FlowBus ?= null
        @JvmStatic
        fun getDefault(): FlowBus {
            if(null == instance){
                synchronized(FlowBus::class.java){
                    if(null == instance){
                        instance = FlowBus()
                    }
                }
            }
            return instance!!
        }
    }

    internal fun initialize(app: Application){
        app.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks{
            override fun onActivityCreated(p0: Activity, p1: Bundle?) {
                activityMap[p0.javaClass.canonicalName] = p0
            }

            override fun onActivityStarted(p0: Activity) {

            }

            override fun onActivityResumed(p0: Activity) {

            }

            override fun onActivityPaused(p0: Activity) {

            }

            override fun onActivityStopped(p0: Activity) {

            }

            override fun onActivitySaveInstanceState(p0: Activity, p1: Bundle) {

            }

            override fun onActivityDestroyed(p0: Activity) {
                activityMap.remove(p0.javaClass.canonicalName)
            }

        })
    }

    fun register(obj: Any){
        val activityName = obj.javaClass.canonicalName ?: ""
        if(subscribers.containsValue(activityName).not()){
            subscribers.put(activityIndex , activityName)
            activityIndex ++
        }
    }



    fun unRegister(obj: Any){
        val activityName = obj.javaClass.canonicalName ?: ""
        if(subscribers.containsValue(activityName)){
            val indexOfActivity = subscribers.indexOfValue(activityName)
            subscribers.remove(indexOfActivity)
        }
    }


    /**
     * 发起事件
     *
     * @param tag
     * @param obj
     */
    fun post(tag: String , obj: Any){
        flowDataDeque.put(FlowData(tag , obj))
    }


    /**
     * 解析
     *
     * @param busData
     */
    @Suppress("UNCHECKED_CAST")
    private fun parseBusInfo(busData: FlowData) {
        val activityIterator = subscribers.valueIterator()
        while(activityIterator.hasNext()){
            val activity = activityIterator.next()
            try {
                val activityClazz = Class.forName(activity)
                CoroutineScope(Dispatchers.IO).launch {
                    findBusMethodAndInvoke(activityClazz as Class<out Activity> , busData)
                }
            }catch (e: ClassCastException){
                e.printStackTrace()
            }
        }
    }

    private fun findBusMethodAndInvoke(clazz: Class<out Activity>, busData: FlowData) {
        val methodIterator = clazz.declaredMethods.iterator()
        val activity = activityMap[clazz.canonicalName]
        while (methodIterator.hasNext()){
            val method = methodIterator.next()
            if(method.isAnnotationPresent(Bus::class.java)){
                if(method.getAnnotation(Bus::class.java)?.tag ?: "" == busData.tag){
                    method.isAccessible = true
                    when(method.getAnnotation(Bus::class.java)?.threadMode){
                        ThreadMode.MAIN -> {
                            CoroutineScope(Dispatchers.Main).launch{
                                method.invoke(activity , busData.obj)
                            }
                        }

                        ThreadMode.IO -> {
                            CoroutineScope(Dispatchers.IO).launch {
                                method.invoke(activity , busData.obj)
                            }
                        }
                    }

                }
            }
        }
    }



    /**
     * 数据包装成flow
     *
     * @return
     */
    private fun FlowData.asFlow(): Flow<FlowData> = flow {
        emit(this@asFlow)
    }
}